#ifndef __IMAGE_H__
#define __IMAGE_H__


#include "RGBColor.h"
#include <math.h>


#define USE_NEAREST_NEIGHBOUR

namespace Engine
{
	class Image
	{
	public:

		Image()
		{}

		Image(int p_width, int p_height)
		{
			m_imagePixels = new RGBColor[p_width * p_height];
			m_width = p_width;
			m_height = p_height;
		}

		inline float TruncateValue(float p_inputValue)
		{
			float interm = (int)p_inputValue;
			float secInterm = p_inputValue - interm;
			return secInterm < 0 ? 1 + secInterm : secInterm;
		}

		void CreateDither(void)
		{
			int currentX = 0;
			int currentY = 0;

			for (int index = 0; index < m_width * m_height; index++)
			{
				if ((currentX % 2 != 0 && currentY % 2 != 0) || (currentX == currentY))
					m_imagePixels[index] = RGBColor(1, 0, 0);
				else
					m_imagePixels[index] = RGBColor(0, 0, 1);

				currentX++;
				if (currentX == m_width)
				{
					currentX = 0;
					currentY++;
				}
			}
		}



		//bilinear filtering being implemented
		//https://en.wikipedia.org/wiki/Bilinear_filtering
		RGBColor GetColor(float p_xCoor, float p_yCoor)
		{
			//float u = TruncateValue(p_xCoor);
			//float v = TruncateValue(p_yCoor);
			//float u1 = u * m_width - 0.5;
			//float v1 = v * m_height - 0.5;
			//int x = floor(u);
			//int y = floor(v);
			//double u_ratio = u - x;
			//double v_ratio = v - y;
			//double u_opposite = 1 - u_ratio;
			//double v_opposite = 1 - v_ratio
			//int currentLocation = x + y * m_width;
			//
			//	 double result = (tex[x][y]   * u_opposite  + tex[x+1][y]   * u_ratio) * v_opposite + (tex[x][y+1] * u_opposite  + tex[x+1][y+1] * u_ratio) * v_ratio;
			//if (currentLocation > 0)
			//{
			//	RGBColor output = (m_imagePixels[currentLocation] * u_opposite + (x != m_width - 1 ? m_imagePixels[(x + 1) + y * m_width] : RGBColor(0))) * v_opposite;
			//	output = output + (y != m_height - 1 ? m_imagePixels[(x)+(y + 1) * m_width] : RGBColor(0)) * u_opposite;
			//	output = output + ((y != m_height - 1 && x != m_width - 1) ? m_imagePixels[(x + 1) + (y + 1) * m_width] : RGBColor(0)) * v_ratio;
			//	return output;
			//}
			//else
			//{
			//	return RGBColor(0);
			//}

			//nearest neighbour
			float u = TruncateValue(p_xCoor);
			float v = TruncateValue(p_yCoor);

			int _x = u * m_width;
			int _y = v * m_height;

			int index = _y * m_width + _x;
			return m_imagePixels[index];

			float u1 = u * m_width;
			float v1 = v * m_height;

			int location = u1 + v1 * m_width;

			if (location >= m_width*m_height)
			{
				location = m_width * m_height - 1;
			}

			RGBColor outputColor = m_imagePixels[location];

			return outputColor;
		}

		RGBColor GetColor(int p_x, int p_y)
		{
			int index = p_x + p_y * m_width;
			return m_imagePixels[index];
		}

		int GetArea()
		{
			return (m_width * m_height);
		}

		//TODO impliment biliear
		RGBColor GetColorBilinear(int p_x, int p_y)
		{
			return m_imagePixels[p_y + p_x];
		}

		RGBColor GetNearestNeighbour(int p_x, int p_y)
		{
			return m_imagePixels[p_y + p_x];
		}

		void SetPixel(int p_Index, RGBColor p_pixelColor)
		{
			m_imagePixels[p_Index] = p_pixelColor;
		}

		void SetPixel(int p_xCoor, int p_yCoor, RGBColor p_pixelColor)
		{
			int x = p_xCoor;
			int y = p_yCoor * m_width;
			m_imagePixels[x + y] = p_pixelColor;
		}

		int GetHeight()
		{
			return m_height;
		}

		int GetWidth()
		{
			return m_width;
		}

		void MakeTestCard()
		{
			RGBColor cardHues[9] = { RGBColor(1), RGBColor(1, 0, 1), RGBColor(0, 1, 0),
				RGBColor(0, 0, 1), RGBColor(1, 0, 0), RGBColor(0, 0, 1),
				RGBColor(0, 1, 0), RGBColor(0, 1, 1), RGBColor(1) };

			for (int y = 0; y < this->GetHeight(); ++y)
			{
				for (int x = 0; x < this->GetWidth(); ++x)
				{
					RGBColor pixel = cardHues[(int)((((float)x) / (this->GetWidth() + 1)) * 8.f)];
					this->SetPixel(x, y, pixel);
				}
			}
		}
		void WriteToFile(string p_path)
		{
			try
			{
				int total = m_width * m_height;
				const char * charPath = p_path.c_str();

				ofstream fp(charPath, ios::out | ios::binary);
				fp << "P6\n" << m_width << " " << m_height << "\n255\n";
				for (int index = 0; index < total; index++)
				{
					RGBColor currentColor = m_imagePixels[index];

					//writing each pixel ot file 
					fp << (unsigned char)(min(float(1), currentColor.r) * 255) <<
						(unsigned char)(min(float(1), currentColor.g) * 255) <<
						(unsigned char)(min(float(1), currentColor.b) * 255);
				}

				fp.close();
			}
			catch (exception e)
			{
				std::cerr << e.what() << endl;
			}
		}

		~Image()
		{
			delete[] m_imagePixels;
		}


	private:

		int m_width;
		int m_height;
		RGBColor* m_imagePixels;
	};
}

#endif // !__IMAGE_H__
